﻿//#define SerialPortBaseDebugMessage

using HARTCalibrationTool.Commands;
using HARTCalibrationTool.Commands.BatchCalibrationBoard;
using HARTCalibrationTool.Observers;
using System;
using System.Collections.Generic;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace HARTCalibrationTool.SerialPortManager
{
    public class SerialPortsBase
    {
        private CommandsBase _currentCMD;
        protected SerialPort _serialPort = new SerialPort();
        protected Dictionary<string, IObserver> _observers = new Dictionary<string, IObserver>();
        private List<byte> _rxDataBuffer = new List<byte>();
        private static TextBox _debugMsgControl = null;

        public event EventHandler CommandCompleted;

        public SerialPortsBase() 
        {  
            _serialPort.BaudRate = 19200;
            _serialPort.Parity = Parity.None;
            _serialPort.StopBits = StopBits.One;
            _serialPort.DataBits = 8;

            _serialPort.DataReceived += new SerialDataReceivedEventHandler(OnDataReceived);
    }

        public CommandsBase CurrentCMD
        { 
            get { return _currentCMD; }
        }

        public async void OnDataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            int bytesToRead = _serialPort.BytesToRead;
            byte[] buffer = new byte[bytesToRead];
            byte[] data;
            _serialPort.Read(buffer, 0, bytesToRead);

            WriteRxDataBuffer(buffer);

            data = GetRxBufferBytesData();

#if SerialPortBaseDebugMessage
            debugMsgManager.PrintMessage("OnDataReceived: <--- " + Utils.BytesArrayToHexString(data, prefix: "", suffix: ", "));
#endif
            if (_currentCMD == null)
            { 
                ClearRxDataBuffer();
                return;
            }

            if (_currentCMD?.VerifyDataFrameIntegrity(data) == VerifyDataFrameStatus.eSuccess)
            {
                await _currentCMD?.ParseResponse(data);
                ClearRxDataBuffer();
                CommandCompleted?.Invoke(this, EventArgs.Empty);
            }
            else if (_currentCMD.VerifyDataFrameIntegrity(data) == VerifyDataFrameStatus.eWaiting)
            {

            }
            else
            {
                ClearRxDataBuffer();
            }
        }

        public virtual async Task SerialPortWrite(byte[] cmdCode)
        {
            await Task.Run(() => {
                if (_serialPort.IsOpen)
                {
                    _serialPort.Write(cmdCode, 0, cmdCode.Length);
                }
            });
        }

        public async Task SerialPortWriteLine(string cmdString)
        {
            await Task.Run(() => {
                if (_serialPort.IsOpen)
                {
                    _serialPort.WriteLine(cmdString);
                }
            });
        }

        public bool Open(string portName)
        {
            try
            {
                _serialPort.PortName = portName;
                _serialPort?.Open();
            }
            catch
            {
                return false;
            }

            return true;
        }

        public bool Close() 
        {
            try
            {
                _serialPort?.Close();
            }
            catch
            {
                return false;
            }

            return true;
        }

        public void SetCurrentCMD(CommandsBase command)
        {
            _currentCMD = command;
        }

        private void WriteRxDataBuffer(byte[] data)
        {
            _rxDataBuffer.AddRange(data);
        }

        public void ClearRxDataBuffer()
        {
            _rxDataBuffer.Clear();
        }

        public byte[] GetRxBufferBytesData()
        {
            return _rxDataBuffer.ToArray();
        }

        public string[] GetSerialPortNames()
        { 
            return SerialPort.GetPortNames();
        }

        public bool SerialPortIsOpen()
        { 
            return _serialPort.IsOpen;
        }

        public void RegisterObservers(string observerName, IObserver observerObj)
        {
            if ((string.IsNullOrWhiteSpace(observerName)) || (observerObj == null))
            {
                return;
            }

            if (_observers.ContainsKey(observerName))
            {
                UnRegisgerObserver(observerName);
            }
            _observers.Add(observerName, observerObj);
        }

        public void UnRegisgerObserver(string observerName)
        { 
            _observers.Remove(observerName);
        }

        public void NotifyObserver(string observerName, List<string> listStr)
        {
            if (_observers.ContainsKey(observerName))
            {
                _observers[observerName].Update(listStr);
            }
        }
        public static void SetDebugMessageContrl(TextBox control)
        {
            _debugMsgControl = control;
        }

        public static void PrintMessage(string msg)
        {
            if (_debugMsgControl != null)
            {
                if (_debugMsgControl.InvokeRequired)
                {
                    _debugMsgControl?.Invoke(new Action<string>(PrintMessage), msg);
                }
                else
                {
                    _debugMsgControl?.AppendText("[" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "]:" + msg + "\r\n");
                }
            }
        }

        public static void PrintMessageNoTime(string msg)
        {
            if (_debugMsgControl != null)
            {
                if (_debugMsgControl.InvokeRequired)
                {
                    _debugMsgControl?.Invoke(new Action<string>(PrintMessage), msg);
                }
                else
                {
                    _debugMsgControl?.AppendText(msg + "\r\n");
                }
            }
        }
    }
}
